
<!DOCTYPE html
  PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   
      <title>8.4.&nbsp;BaseHTMLProcessor.py 介绍</title>
      <link rel="stylesheet" href="../diveintopython.css" type="text/css">
      <link rev="made" href="mailto:f8dy@diveintopython.org">
      <meta name="generator" content="DocBook XSL Stylesheets V1.52.2">
      <meta name="keywords" content="Python, Dive Into Python, tutorial, object-oriented, programming, documentation, book, free">
      <meta name="description" content="Python from novice to pro">
      <link rel="home" href="../toc/index.html" title="Dive Into Python">
      <link rel="up" href="index.html" title="第&nbsp;8&nbsp;章&nbsp;HTML 处理">
      <link rel="previous" href="extracting_data.html" title="8.3.&nbsp;从 HTML 文档中提取数据">
      <link rel="next" href="locals_and_globals.html" title="8.5.&nbsp;locals 和 globals">
   </head>
   <body>
      <table id="Header" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td id="breadcrumb" colspan="5" align="left" valign="top">导航：<a href="../index.html">起始页</a>&nbsp;&gt;&nbsp;<a href="../toc/index.html">Dive Into Python</a>&nbsp;&gt;&nbsp;<a href="index.html">HTML 处理</a>&nbsp;&gt;&nbsp;<span class="thispage">BaseHTMLProcessor.py 介绍</span></td>
            <td id="navigation" align="right" valign="top">&nbsp;&nbsp;&nbsp;<a href="extracting_data.html" title="上一页: “从 HTML 文档中提取数据”">&lt;&lt;</a>&nbsp;&nbsp;&nbsp;<a href="locals_and_globals.html" title="下一页: “locals 和 globals”">&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3" id="logocontainer">
               <h1 id="logo"><a href="../index.html" accesskey="1">深入 Python :Dive Into Python 中文版</a></h1>
               <p id="tagline">Python 从新手到专家 [Dip_5.4b_CPyUG_Release]</p>
            </td>
            <td colspan="3" align="right">
               <form id="search" method="GET" action="http://www.google.com/custom">
                  <p><label for="q" accesskey="4">Find:&nbsp;</label><input type="text" id="q" name="q" size="20" maxlength="255" value=""> <input type="submit" value="搜索"><input type="hidden" name="domains" value="woodpecker.org.cn/diveintopython"><input type="hidden" name="sitesearch" value="www.woodpecker.org.cn/diveintopython"></p>
               </form>
            </td>
         </tr>
      </table>
      <!--#include virtual="/inc/ads" -->
      <div class="section" lang="zh_cn">
         <div class="titlepage">
            <div>
               <div>
                  <h2 class="title"><a name="dialect.basehtml"></a>8.4.&nbsp;<tt class="filename">BaseHTMLProcessor.py</tt> 介绍
                  </h2>
               </div>
            </div>
            <div></div>
         </div>
         <div class="abstract">
            <p><tt class="classname">SGMLParser</tt> 自身不会产生任何结果。它只是分析，分析，再分析，对于它找到的有趣的东西会调用相应的一个方法，但是这些方法什么都不做。<tt class="classname">SGMLParser</tt> 是一个 <span class="acronym">HTML</span> <span class="emphasis"><em>消费者 (consumer)</em></span>：它接收 <span class="acronym">HTML</span>，将其分解成小的、结构化的小块。正如您所看到的，在<a href="extracting_data.html" title="8.3.&nbsp;从 HTML 文档中提取数据">前一节</a>中，您可以定义 <tt class="classname">SGMLParser</tt> 的子类，它可以捕捉特别标记和生成有用的东西，如一个网页中所有链接的一个列表。现在我们将沿着这条路更深一步。我们要定义一个可以捕捉 <tt class="classname">SGMLParser</tt> 所丢出来的所有东西的一个类，接着重建整个 <span class="acronym">HTML</span> 文档。用技术术语来说，这个类将是一个 <span class="acronym">HTML</span> <span class="emphasis"><em>生产者 (producer)</em></span>。
            </p>
         </div>
         <p><tt class="classname">BaseHTMLProcessor</tt> 子类化 <tt class="classname">SGMLParser</tt>，并且提供了全部的 8 个处理方法：<tt class="function">unknown_starttag</tt>、<tt class="function">unknown_endtag</tt>、<tt class="function">handle_charref</tt>、<tt class="function">handle_entityref</tt>、<tt class="function">handle_comment</tt>、<tt class="function">handle_pi</tt>、<tt class="function">handle_decl</tt> 和 <tt class="function">handle_data</tt>。
         </p>
         <div class="example"><a name="dialect.basehtml.intro"></a><h3 class="title">例&nbsp;8.8.&nbsp;<tt class="classname">BaseHTMLProcessor</tt> 介绍
            </h3><pre class="programlisting"><span class='pykeyword'>
class</span> BaseHTMLProcessor(SGMLParser):
    <span class='pykeyword'>def</span><span class='pyclass'> reset</span>(self):                        <a name="dialect.basehtml.1.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
        self.pieces = []
        SGMLParser.reset(self)

    <span class='pykeyword'>def</span><span class='pyclass'> unknown_starttag</span>(self, tag, attrs): <a name="dialect.basehtml.1.2"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12">
        strattrs = <span class='pystring'>""</span>.join([<span class='pystring'>' %s="%s"'</span> % (key, value) <span class='pykeyword'>for</span> key, value <span class='pykeyword'>in</span> attrs])
        self.pieces.append(<span class='pystring'>"&lt;%(tag)s%(strattrs)s&gt;"</span> % locals())

    <span class='pykeyword'>def</span><span class='pyclass'> unknown_endtag</span>(self, tag):          <a name="dialect.basehtml.1.3"></a><img src="../images/callouts/3.png" alt="3" border="0" width="12" height="12">
        self.pieces.append(<span class='pystring'>"&lt;/%(tag)s&gt;"</span> % locals())

    <span class='pykeyword'>def</span><span class='pyclass'> handle_charref</span>(self, ref):          <a name="dialect.basehtml.1.4"></a><img src="../images/callouts/4.png" alt="4" border="0" width="12" height="12">
        self.pieces.append(<span class='pystring'>"&amp;#%(ref)s;"</span> % locals())

    <span class='pykeyword'>def</span><span class='pyclass'> handle_entityref</span>(self, ref):        <a name="dialect.basehtml.1.5"></a><img src="../images/callouts/5.png" alt="5" border="0" width="12" height="12">
        self.pieces.append(<span class='pystring'>"&amp;%(ref)s"</span> % locals())
        <span class='pykeyword'>if</span> htmlentitydefs.entitydefs.has_key(ref):
            self.pieces.append(<span class='pystring'>";"</span>)

    <span class='pykeyword'>def</span><span class='pyclass'> handle_data</span>(self, text):            <a name="dialect.basehtml.1.6"></a><img src="../images/callouts/6.png" alt="6" border="0" width="12" height="12">
        self.pieces.append(text)

    <span class='pykeyword'>def</span><span class='pyclass'> handle_comment</span>(self, text):         <a name="dialect.basehtml.1.7"></a><img src="../images/callouts/7.png" alt="7" border="0" width="12" height="12">
        self.pieces.append(<span class='pystring'>"&lt;!--%(text)s--&gt;"</span> % locals())

    <span class='pykeyword'>def</span><span class='pyclass'> handle_pi</span>(self, text):              <a name="dialect.basehtml.1.8"></a><img src="../images/callouts/8.png" alt="8" border="0" width="12" height="12">
        self.pieces.append(<span class='pystring'>"&lt;?%(text)s&gt;"</span> % locals())

    <span class='pykeyword'>def</span><span class='pyclass'> handle_decl</span>(self, text):
        self.pieces.append(<span class='pystring'>"&lt;!%(text)s&gt;"</span> % locals())</pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left"><tt class="function">reset</tt> 由 <tt class="function">SGMLParser.__init__</tt> 来调用。在<a href="../object_oriented_framework/defining_classes.html#fileinfo.init.code.example" title="例&nbsp;5.6.&nbsp;编写 FileInfo 类">调用父类方法</a>之前将 <tt class="varname">self.pieces</tt> 初始化为空列表。<tt class="varname">self.pieces</tt> 是一个<a href="../object_oriented_framework/userdict.html#fileinfo.userdict.init.example" title="例&nbsp;5.9.&nbsp;定义 UserDict 类">数据属性</a>，将用来保存将要构造的 <span class="acronym">HTML</span> 文档的片段。每个处理器方法都将重构 <tt class="classname">SGMLParser</tt> 所分析出来的 <span class="acronym">HTML</span>，并且每个方法将生成的字符串追加到 <tt class="varname">self.pieces</tt> 之后。注意，<tt class="varname">self.pieces</tt> 是一个 list。也许您想将它定义为一个字符串，然后不停地将每个片段追加到它的后面。这样做是可以的，但是 <span class="application">Python</span> 在处理 list 方面效率更高一些。
                        
                        <sup>[<a name="d0e20874" href="#ftn.d0e20874">5</a>]</sup></td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.2"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">因为 <tt class="classname">BaseHTMLProcessor</tt> 没有为特别标记定义方法 (如在 <a href="extracting_data.html#dialect.extract.links" title="例&nbsp;8.6.&nbsp;urllister.py 介绍"><tt class="classname">URLLister</tt></a> 中的<tt class="function">start_a</tt> 方法)，
                        <tt class="classname">SGMLParser</tt> 将对每一个开始标记调用 <tt class="function">unknown_starttag</tt> 方法。这个方法接收标记 (<tt class="varname">tag</tt>) 和属性的名字/值对的 list(<tt class="varname">attrs</tt>) 两参数，重新构造初始的 <span class="acronym">HTML</span>，接着将结果追加到 <tt class="varname">self.pieces</tt> 后。 这里的<a href="../native_data_types/formatting_strings.html" title="3.5.&nbsp;格式化字符串">字符串格式化</a>有些陌生，我们将留到下一节再说明。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.3"><img src="../images/callouts/3.png" alt="3" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">重构结束标记要简单得多，只是使用标记名字，把它包在 <tt class="literal">&lt;/...&gt;</tt> 括号中。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.4"><img src="../images/callouts/4.png" alt="4" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">当 <tt class="classname">SGMLParser</tt> 找到一个字符引用时，会用原始的引用来调用 <tt class="function">handle_charref</tt>。如果 <span class="acronym">HTML</span> 文档包含 <tt class="literal">&amp;#160;</tt> 这个引用，<tt class="varname">ref</tt> 将为 <tt class="literal">160</tt>。重构原始的完整的字符引用只要将 <tt class="varname">ref</tt> 包装在 <tt class="literal">&amp;#...;</tt> 字符中间。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.5"><img src="../images/callouts/5.png" alt="5" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">实体引用同字符引用相似，但是没有#号。重建原始的实体引用只要将 <tt class="varname">ref</tt> 包装在 <tt class="literal">&amp;...;</tt> 字符串中间。(实际上，一位博学的读者曾经向我指出，除些之外还稍微有些复杂。仅有某种标准的 <span class="acronym">HTML</span> 实体以一个分号结束；其它看上去差不多的实体并不如此。幸运的是，标准 <span class="acronym">HTML</span> 实体集已经定义在 <span class="application">Python</span> 的一个叫做 <tt class="filename">htmlentitydefs</tt> 的模块中了。从而引出额外的 <tt class="literal">if</tt> 语句。) 
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.6"><img src="../images/callouts/6.png" alt="6" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">文本块则简单地不经修改地追加到 <tt class="varname">self.pieces</tt> 后。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.7"><img src="../images/callouts/7.png" alt="7" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left"><span class="acronym">HTML</span> 注释包装在 <tt class="literal">&lt;!--...--&gt;</tt> 字符中。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.1.8"><img src="../images/callouts/8.png" alt="8" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">处理指令包装在 <tt class="literal">&lt;?...&gt;</tt> 字符中。
                     </td>
                  </tr>
               </table>
            </div>
         </div><a name="d0e21013"></a><table class="important" border="0" summary="">
            <tr>
               <td rowspan="2" align="center" valign="top" width="1%"><img src="../images/important.png" alt="重要" title="" width="24" height="24"></td>
            </tr>
            <tr>
               <td colspan="2" align="left" valign="top" width="99%"><span class="acronym">HTML</span> 规范要求所有非 <span class="acronym">HTML</span> (像客户端的 <span class="application">JavaScript</span>) 必须包括在 <span class="acronym">HTML</span> 注释中，但不是所有的页面都是这么做的 (而且所有的最新的浏览器也都容许不这样做) 。<tt class="classname">BaseHTMLProcessor</tt> 不允许这样，如果脚本嵌入得不正确，它将被当作 <span class="acronym">HTML</span> 一样进行分析。例如，如果脚本包含了小于和等于号，<tt class="classname">SGMLParser</tt> 可能会错误地认为找到了标记和属性。<tt class="classname">SGMLParser</tt> 总是把标记名和属性名转换成小写，这样可能破坏了脚本，并且 <tt class="classname">BaseHTMLProcessor</tt> 总是用双引号来将属性封闭起来 (尽管原始的 <span class="acronym">HTML</span> 文档可能使用单引号或没有引号) ，这样必然会破坏脚本。应该总是将您的客户端脚本放在 <span class="acronym">HTML</span> 注释中进行保护。
               </td>
            </tr>
         </table>
         <div class="example"><a name="dialect.output.example"></a><h3 class="title">例&nbsp;8.9.&nbsp;<tt class="classname">BaseHTMLProcessor</tt> 输出结果
            </h3><pre class="programlisting">
    <span class='pykeyword'>def</span><span class='pyclass'> output</span>(self):               <a name="dialect.basehtml.2.1"></a><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12">
        <span class='pystring'>"""Return processed HTML as a single string"""</span>
        <span class='pykeyword'>return</span> <span class='pystring'>""</span>.join(self.pieces) <a name="dialect.basehtml.2.2"></a><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></pre><div class="calloutlist">
               <table border="0" summary="Callout list">
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.2.1"><img src="../images/callouts/1.png" alt="1" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">这是在 <tt class="classname">BaseHTMLProcessor</tt> 中的一个方法，它永远不会被父类 <tt class="classname">SGMLParser</tt> 所调用。因为其它的处理器方法将它们重构的 <span class="acronym">HTML</span> 保存在 <tt class="varname">self.pieces</tt> 中，这个函数需要将所有这些片段连接成一个字符串。正如前面提到的，<span class="application">Python</span> 在处理列表方面非常出色，但对于字符串处理就逊色了。所以我们只有在某人确实需要它时才创建完整的字符串。
                     </td>
                  </tr>
                  <tr>
                     <td width="12" valign="top" align="left"><a href="#dialect.basehtml.2.2"><img src="../images/callouts/2.png" alt="2" border="0" width="12" height="12"></a> 
                     </td>
                     <td valign="top" align="left">如果您愿意，也可以换成使用 <tt class="filename">string</tt> 模块的 <tt class="function">join</tt> 方法：<tt class="literal">string.join(self.pieces, "")</tt>。
                     </td>
                  </tr>
               </table>
            </div>
         </div>
         <div class="furtherreading">
            <h3>进一步阅读</h3>
            <ul>
               <li><a href="http://www.w3.org/">W3C</a> 讨论了<a href="http://www.w3.org/TR/REC-html40/charset.html#entities">字符和实体引用</a>。
               </li>
               <li><a href="http://www.python.org/doc/current/lib/"><i class="citetitle"><span class="application">Python</span> Library Reference</i></a> 解答了您的怀疑，即 <a href="http://www.python.org/doc/current/lib/module-htmlentitydefs.html"><tt class="filename">htmlentitydefs</tt> 模块</a>的确名符其实。
               </li>
            </ul>
         </div>
         <div class="footnotes">
            <h3 class="footnotetitle">Footnotes</h3>
            <div class="footnote">
               <p><sup>[<a name="ftn.d0e20874" href="#d0e20874">5</a>] </sup><span class="application">Python</span> 处理 list 比字符串快的原因是：list 是可变的，但字符串是不可变的。这就是说向 list 进行追加只是增加元素和修改索引。因为字符串在创建之后不能被修改，像 <tt class="literal">s = s + newpiece</tt> 这样的代码将会从原值和新片段的连接结果中创建一个全新的字符串，然后丢弃原来的字符串。这样就需要大量昂贵的内存管理，并且随着字符串变长，所需要的开销也在增长。所以在一个循环中执行 <tt class="literal">s = s + newpiece</tt> 非常不好。用技术术语来说，向一个 list 追加 <tt class="varname">n</tt> 个项的代价为 <tt class="literal">O(n)</tt>，而向一个字符串追加 <tt class="varname">n</tt> 个项的代价是 <tt class="literal">O(n<sup>2</sup>)</tt>。
               </p>
            </div>
         </div>
      </div>
      <table class="Footer" width="100%" border="0" cellpadding="0" cellspacing="0" summary="">
         <tr>
            <td width="35%" align="left"><br><a class="NavigationArrow" href="extracting_data.html">&lt;&lt;&nbsp;从 HTML 文档中提取数据</a></td>
            <td width="30%" align="center"><br>&nbsp;<span class="divider">|</span>&nbsp;<a href="index.html#dialect.divein" title="8.1.&nbsp;概览">1</a> <span class="divider">|</span> <a href="introducing_sgmllib.html" title="8.2.&nbsp;sgmllib.py 介绍">2</a> <span class="divider">|</span> <a href="extracting_data.html" title="8.3.&nbsp;从 HTML 文档中提取数据">3</a> <span class="divider">|</span> <span class="thispage">4</span> <span class="divider">|</span> <a href="locals_and_globals.html" title="8.5.&nbsp;locals 和 globals">5</a> <span class="divider">|</span> <a href="dictionary_based_string_formatting.html" title="8.6.&nbsp;基于 dictionary 的字符串格式化">6</a> <span class="divider">|</span> <a href="quoting_attribute_values.html" title="8.7.&nbsp;给属性值加引号">7</a> <span class="divider">|</span> <a href="dialect.html" title="8.8.&nbsp;dialect.py 介绍">8</a> <span class="divider">|</span> <a href="all_together.html" title="8.9.&nbsp;全部放在一起">9</a> <span class="divider">|</span> <a href="summary.html" title="8.10.&nbsp;小结">10</a>&nbsp;<span class="divider">|</span>&nbsp;
            </td>
            <td width="35%" align="right"><br><a class="NavigationArrow" href="locals_and_globals.html">locals 和 globals&nbsp;&gt;&gt;</a></td>
         </tr>
         <tr>
            <td colspan="3"><br></td>
         </tr>
      </table>
      <div class="Footer">
         <p class="copyright">Copyright © 2000, 2001, 2002, 2003, 2004 <a href="mailto:mark@diveintopython.org">Mark Pilgrim</a></p>
         <p class="copyright">Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007 <a href="mailto:python-cn@googlegroups.com">CPyUG (邮件列表)</a></p>
      </div>
   </body>
</html>